# -*- coding: utf-8 -*-


#6.1基于scikit-image形态学模块的形态学图像处理
#6.1.1对二值图像的操作
#腐蚀，腐蚀是一种基本的形态学操作，他可以缩小前景对象的大小，平滑图像边界，并删除图形和小的图像
import numpy as np
from skimage import data
from skimage.io import imread
from skimage.color import rgb2gray
import matplotlib.pylab as pylab
from skimage.morphology import binary_erosion,rectangle
from skimage.morphology import binary_dilation,disk
from skimage import img_as_float
from skimage.morphology import binary_opening,binary_closing
from skimage.morphology import skeletonize
from skimage.morphology import white_tophat,black_tophat,square 
def plot_image(image,title=''):
    pylab.rcParams['font.sans-serif']=['FangSong']
    pylab.rcParams['axes.unicode_minus']=False
    pylab.title(title,size=20)
    pylab.imshow(image)
    pylab.axis('off')
    
def plot_images_horizontally(original,filtered,filtered_name,sz=(18,7)):
    pylab.rcParams['font.sans-serif']=['FangSong']
    pylab.rcParams['axes.unicode_minus']=False
    pylab.gray()
    pylab.figure(figsize=sz)
    pylab.subplot(1,2,1)
    plot_image(original,'原始图像')
    pylab.subplot(1,2,2)
    plot_image(filtered,filtered_name)
    pylab.show()

im=rgb2gray(imread('clock2.jpg'))
im[im<=0.5]=0
im[im>0.5]=1
pylab.gray()
pylab.figure(figsize=(10,5))
pylab.subplot(1,3,1)
plot_image(im,'原始图像')
im1=binary_erosion(im,rectangle(1,5))
pylab.subplot(1,3,2)
plot_image(im1,'矩形大小为(1,5)的腐蚀')
im2=binary_erosion(im,rectangle(1,30))
pylab.subplot(1,3,3)
plot_image(im1,'矩形大小为(1,30)的腐蚀')
pylab.show()

#膨胀

im=img_as_float(imread('tagore.jpg'))
im=1-im#去图片
im[im<=0.5]=0
im[im>0.5]=1
pylab.gray()
pylab.figure(figsize=(12,6))
pylab.subplot(131)
pylab.imshow(im)
pylab.title('原始图像',size=15)
pylab.axis('off')
for d in range(1,3):
    pylab.subplot(1,3,d+1)
    im1=binary_dilation(im,disk(2*d))
    pylab.imshow(im1)
    pylab.title('磁盘结构元素尺寸为'+str(2*d)+'的膨胀',size=15)
    pylab.axis('off')
pylab.show() 
    
 #开闭运算
#开运算是一种形态学运算，可以表示为先腐蚀后膨胀运算的组合，他从二值图像中删除小对象。
#闭运算是另一种形态学运算，可以表示为先膨胀后腐蚀运算的组合，他从二值图像中删除小洞   
    

im=rgb2gray(imread('circles.jpg')) 
im[im<=0.5]=0
im[im>0.5]=1
pylab.gray()
pylab.figure(figsize=(12,8))
pylab.subplot(1,3,1)
plot_image(im,'原始图像')
im1=binary_opening(im,disk(12))
pylab.subplot(1,3,2)
plot_image(im,'磁盘结构元素尺寸为12的开运算')
im2=binary_closing(im,disk(6))
pylab.subplot(1,3,3)
plot_image(im2,'磁盘结构元素尺寸为6的闭运算')
pylab.show()

#分别用binary_erosion()和binary_expand()替换binary_opening()和binary_closing()

 
 
 
#骨架化
#用形态学细化操作将二值图像中的每个连接组件简化为单个像素宽的骨架


im=img_as_float(imread('dynasaur.jpg'))[...,2]
im[im<=0.5]=0
im[im>0.5]=1
skeleton=skeletonize(im)
plot_images_horizontally(im,skeleton,'骨架化处理图像',sz=(10,8))
 
 
 ##凸包运算
from skimage.morphology import convex_hull_image
im=rgb2gray(imread('tubao.jpg')) 
im[im<=0.5]=0
im[im>0.5]=1
chull=convex_hull_image(im) 
plot_images_horizontally(im,chull,'凸包计算',sz=(10,8))
 
#绘制原始二值图像和计算得到的凸包图像的差值图像
im=im.astype(np.bool)
chull_diff=img_as_float(chull.copy())
chull_diff[im]=2
pylab.figure(figsize=(10,8))
pylab.imshow(chull_diff,cmap='gray',interpolation='nearest')
pylab.title('原始_凸包差值图像',size=20)
pylab.show()


##删除小对象
#使用remove_smalll_objects()函数删除小于阈值的对象，制定的阈值越大，删除对象越多
from skimage.morphology import remove_small_objects
im=rgb2gray(imread('circles.jpg'))
im[im<=0.5]=0
im[im>0.5]=1
im=im.astype(np.bool)
pylab.figure(figsize=(10,8))
pylab.subplot(2,2,1)
plot_image(im,'原始图像')
i=2
for osz in [50,100,150]:
    im1=remove_small_objects(im,osz,connectivity=1)
    pylab.subplot(2,2,i)
    plot_image(im1,'删除尺寸小于{}的目标'.format(osz))
    i+=1
pylab.show()
#结果可以看到这些对象的尺寸都比较小，仅仅是50都已经删除干净了，后面的也就没有意义了


#白丁帽与黑顶帽计算
#图像的白顶帽计算比结构元素更小的亮点，定义为原始图像与其形态学开运算的差值图像
#图像的黑顶帽计算计算比结构元素更小的黑点，定义为原始图像与其形态学闭运算的差值图像。
#原始图像经过黑顶帽操作后变成亮点

im=img_as_float(imread('tagore.jpg'))#对图像进行浮点型转换
pylab.imshow(im)
im[im<=0.5]=0
im[im>0.5]=1
im1=white_tophat(im,square(5))
im2=black_tophat(im,square(5))
pylab.figure(figsize=(10,8))
pylab.subplot(1,2,1)
plot_image(im1,'白顶帽计算')
pylab.subplot(1,2,2)
plot_image(im2,'黑顶帽计算')
pylab.show()

##提取边界
#腐蚀运算可以用来提取二值图像的边界
from skimage.morphology import binary_erosion
im=rgb2gray(imread('tubao.jpg'))
im[im<=0.5]=0
im[im>0.5]=1
boundary=im-binary_erosion(im)
plot_images_horizontally(im,boundary,'边界',sz=(10,8))

##6.1.2 利用开、闭运算实现指纹清洗
#开运算和闭运算可以用于按顺序地从二值图像中（如小的前景对象）去噪。
#开闭运算可以用于清洗指纹图像的预处理步骤
#需要指纹有噪声，选用的图片没有噪声点，所以没啥变化
im=rgb2gray(imread('fingerprint.jpg'))
im[im<=0.5]=0
im[im>0.5]=1
im_o=binary_opening(im,square(2))
im_c=binary_closing(im,square(2))
im_oc=binary_closing(binary_opening(im,square(2)),square(2))
pylab.figure(figsize=(10,8))
pylab.subplot(221)
plot_image(im,'原始图像')
pylab.subplot(222)
plot_image(im_o,'开运算')
pylab.subplot(223)
plot_image(im_c,'闭运算')
pylab.subplot(224)
plot_image(im_oc,'开闭运算')
pylab.show()

#6.1.3灰度级操作
from skimage.morphology import dilation,erosion,closing,opening
im=imread('zebra.png')
im=rgb2gray(im)
struct_elem=square(5)
eroded=erosion(im,struct_elem)
plot_images_horizontally(im,eroded,'腐蚀图像',sz=(10,8))

#应用膨胀
dilated=dilation(im,struct_elem)
plot_images_horizontally(im,dilated,'膨胀图像',sz=(10,8))

#形态学灰度开运算
opened=opening(im,struct_elem)
plot_images_horizontally(im,opened,'开运算图像',sz=(10,8))
#从结果可以看到，虽然去掉了一些细细的白色条纹，但是黑色条纹的宽度并没有随着闭运算而改变

#形态学灰度级闭运算
closed=closing(im,struct_elem)
plot_images_horizontally(im,closed,'闭运算图像',sz=(10,8))
#从结果可以看到，虽然去掉了一些细细的黑色条纹，但是白色条纹的宽度并没有随着闭运算而改变

##6.2基于scikit-image filter.rank 模块的形态学处理
#scikit-image 的 filter.rank 模块提供了实现形态学滤波器的功能


#形态学对比度增强
#形态学对比度增强滤波器通过只考虑由结构元素定义的领域中的像素对每个像素进行操作。
#他用领域内的局部最小或者局部最大像素替换中心像素。这取决于原始像素最接近那个像素
from skimage.filters.rank import enhance_contrast
from skimage import exposure
def plot_gray_image(ax,image,title):
    ax.imshow(image,vmin=0,vmax=255,cmap='gray')
    ax.set_title(title)
    ax.axis('off')
    ax.set_adjustable('box-forced')
image=rgb2gray(imread('squirrel.jpg'))
sigma=0.05
noisy_image=np.clip(image+sigma*np.random.standard_normal(image.shape),0,1)
enhanced_image=enhance_contrast(noisy_image,disk(5))
equalized_image=exposure.equalize_adapthist(noisy_image)
fig,axes=pylab.subplots(1,3,figsize=[10,8],sharex='row',sharey='row')
axes1,axes2,axes3=axes.ravel()
plot_gray_image(axes1,noisy_image,'原始图像')
plot_gray_image(axes2,enhanced_image,'局部形态学对比度增强图像')
plot_gray_image(axes3,equalized_image,'自适应直方图均衡化图像')
pylab.imshow(noisy_image)
pylab.imshow(enhanced_image)
pylab.imshow(equalized_image)


#6.6.2使用中值滤波器去噪
#通过将10%的像素随机设置为255（盐）和10%的像素随机设置为0（椒），将一些脉冲噪声添加到灰度Leana输入图像中

from skimage.filters.rank import median
from skimage.morphology import disk
noisy_image=(rgb2gray(imread('lena.jpg'))*255).astype(np.uint8)
noise=np.random.random(noisy_image.shape)
noisy_image[noise>0.9]=255
noisy_image[noise<0.1]=0
pylab.figure(figsize=(10,8))
pylab.subplot(221)
pylab.title('噪声图像')
pylab.imshow(noisy_image)
pylab.axis('off')

pylab.subplot(222)
pylab.title('中值滤波器 磁盘半径r=1')
pylab.imshow(median(noisy_image,disk(1)))
pylab.axis('off')

pylab.subplot(223)
pylab.title('中值滤波器 磁盘半径r=5')
pylab.imshow(median(noisy_image,disk(5)))
pylab.axis('off')

pylab.subplot(224)
pylab.title('中值滤波器 磁盘半径r=20')
pylab.imshow(median(noisy_image,disk(20)))
pylab.axis('off')
pylab.show()


#6.3基于SciPy ndimage.morphology模块的形态学图像处理
#SciPy 的 ndimage.morphology 模块还提供了前面讨论的用于对二值图像和灰度图像进行形态学操作的函数

#6.1.3填充二值对象中的孔洞
#binary_fill_holes()函数填补了二值对象中的孔洞
#当二值图始黑底白字才可以实现，如果是黑字白底，则得到全黑的图片被填充
from scipy.ndimage.morphology import binary_fill_holes
im=1-(rgb2gray(imread('text1.png'))/255)
im[im<=0.5]=0
im[im>0.5]=1
pylab.figure(figsize=(10,8))
pylab.subplot(221)
pylab.imshow(im)
pylab.title('原始图像',size=10)
pylab.axis('off')
i=2
for n in [3,5,7]:
    pylab.subplot(2,2,i)
    im1=binary_fill_holes(im,structure=np.ones((n,n)))
    pylab.imshow(im1)
    pylab.title('填充的孔数（结构元素为{}）'.format(n))
    pylab.axis('off')
    i+=1
pylab.show()


#6.3.2 采用开闭运算去噪
#利用灰度级的开运算与闭运算从灰度图像中去除椒盐噪声
from scipy import ndimage
im=(rgb2gray(imread('mandrill.jpg'))*255).astype(np.uint8)
noise=np.random.random(im.shape)
im[noise>0.9]=255
im[noise<0.1]=0
im_o=ndimage.grey_opening(im,size=(2,2))
im_c=ndimage.grey_closing(im,size=(2,2))
im_oc=ndimage.grey_closing(ndimage.grey_opening(im,size=(2,2)),size=(2,2))
pylab.rcParams['font.sans-serif']=['FangSong']
pylab.rcParams['axes.unicode_minus']=False
pylab.figure(figsize=(10,8))
pylab.subplot(221)
pylab.imshow(im,cmap='gray')
pylab.title('原始图像',size=10)
pylab.axis('off')

pylab.subplot(222)
pylab.imshow(im_o,cmap='gray')
pylab.title('开运算（去除椒盐噪声）',size=10)
pylab.axis('off')

pylab.subplot(223)
pylab.imshow(im_c,cmap='gray')
pylab.title('闭运算（去除椒盐噪声）',size=10)
pylab.axis('off')

pylab.subplot(224)
pylab.imshow(im_oc,cmap='gray')
pylab.title('开、闭运算（去除椒盐噪声）',size=10)
pylab.axis('off')


#6.3.3计算形态学Beucher梯度
#形态学Beucher梯度计算可定义为输入灰度图像的膨胀运算与腐蚀运算的差值图像

from scipy import ndimage
im=rgb2gray(imread('einstein.jpg'))
im_d=ndimage.grey_dilation(im,size=(3,3))
im_e=ndimage.grey_erosion(im,size=(3,3))
im_bg=im_d-im_e
im_g=ndimage.morphological_gradient(im,size=(3,3))
pylab.gray()
pylab.figure(figsize=(10,8))
pylab.subplot(231)
pylab.imshow(im)
pylab.title('原始图像')
pylab.axis('off')
pylab.subplot(232)
pylab.imshow(im_d)
pylab.title('膨胀')
pylab.axis('off')
pylab.subplot(233)
pylab.imshow(im_e)
pylab.title('腐蚀')
pylab.axis('off')
pylab.subplot(234)
pylab.imshow(im_bg)
pylab.title('Beucher梯度')
pylab.axis('off')
pylab.subplot(235)
pylab.imshow(im_g)
pylab.title('ndimage梯度')
pylab.axis('off')
pylab.subplot(236)
pylab.imshow(im_bg-im_g)
pylab.title('差分梯度')
pylab.axis('off')


##形态学拉普拉斯
#梯度较小的结构元素和拉普拉斯较大的结构元素在提取边缘方面产生了更好的输出图像
im=imread('tagore.jpg')
im_g=ndimage.morphological_gradient(im,size=(3,3))
im_1=ndimage.morphological_laplace(im,size=(5,5))
pylab.figure(figsize=(10,8))
pylab.subplot(121)
pylab.title('形态学拉普拉斯')
pylab.imshow(im_1)
pylab.axis('off')
pylab.subplot(122)
pylab.title('形态学梯度')
pylab.imshow(im_g)
pylab.axis('off')
pylab.show()


